home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / mail / uucp / uubatch.105 / uubatch.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-08  |  21.1 KB  |  834 lines

  1. /*
  2.  * uubatch.c, uubatch version 1.0.5
  3.  *
  4.  * uubatch : a uucp rmail batching program.
  5.  *
  6.  * Authors : Gil Tene         (devil@hellnet.org).
  7.  *           Baruch Cochavy     (blue@dduster.hellnet.org)
  8.  *
  9.  * Usage : 
  10.  *    uubatch [-c] [-B n] [-C n] [-S n] [-T n] node [node2...]
  11.  *
  12.  * This program will attempt to batch the given uucp nodes' queued
  13.  * jobs into uudebat or uucdebat jobs. It takes care of the entire
  14.  * batching process including uucp locking (via an external uucplock
  15.  * program).
  16.  *
  17.  * External uubatch program binaries (specificaly uubatch) are assumed 
  18.  * be runnable with the same path header uubatch was run with (as parsed 
  19.  * from argv[0]). This means that uucplock should usually be in the same
  20.  * directory as uubatch.
  21.  *
  22.  * The program scans each spool directory for C.<> files, which are
  23.  * taken as the names of jobs to consider for batching (if they
  24.  * meet the batching criteria below).
  25.  *
  26.  * uubatch accepts the following parameteres:
  27.  *
  28.  * node, node2... :
  29.  *          The uucp nodes to run on.
  30.  *
  31.  * -c        : compression on (default is off)
  32.  *
  33.  * -B n        : MAX_BYTES = n (default DF_MAX_BYTES)
  34.  *          MAX_BYTES is the maximum size (in bytes) of an rmail
  35.  *           message or a uudebat job that may be added to a batch
  36.  *          Jobs larger than this parameter are not batched.
  37.  *
  38.  * -C n        : MAX_CBYTES = n (default DF_MAX_CBYTES)
  39.  *          MAX_FILE_CBYTES is the maximum size (in bytes) of a
  40.  *          uucdebat job's compressed data file that may be added
  41.  *          to a batch. uucdebat jobs with compressed data files
  42.  *          larger than this parameter are not rebatched.
  43.  *
  44.  * -S n        : CRITICAL_SIZE = n (default DF_CRITICAL_SIZE)
  45.  *          CRITICAL_BATCH_SIZE is the batch size (in bytes)
  46.  *          after which we send the batch off and start a new
  47.  *          one. Be aware of the fact that the actual MAXIMUM
  48.  *          batch size is actually the greater one of :
  49.  *
  50.  *          CRITICAL_BATCH_SIZE + MAX_BYTES_FOR_BATCH
  51.  *
  52.  *          CRITICAL_BATCH_SIZE +
  53.  *            uncompressed(MAX_CBYTES_FOR_BATCH)
  54.  *
  55.  * -T n        : min_time_before_batch = n seconds 
  56.  *                (defaults to MIN_TIME_BEFORE_BATCH)
  57.  *          This is the age of a job (in seconds) before
  58.  *          it may be considered for batching.
  59.  *
  60.  * Each queued job will be examined to see if the job should 
  61.  * be batched. If a job need not be batched then the program simply
  62.  * skips over it. If the job needs to be batched, and the batching
  63.  * succeeds and the batch is sccessfully uuxe'd, the job's data files 
  64.  * are deleted.
  65.  *
  66.  *
  67.  * Batching criteria :
  68.  *
  69.  * rmail jobs with Data less than MAX_BYTES bytes 
  70.  * and more than min_time_before_batch sec. old will be batched.
  71.  *
  72.  * uudebat jobs with Data less than MAX_BYTES and
  73.  * more than min_time_before_batch sec. old will be batched.
  74.  *
  75.  * uucdebat jobs with compressed data less than MAX_CBYTES
  76.  * and more than min_time_before_batch sec. old will be batched.
  77.  *
  78.  * Batching will ONLY be done if at least one rmail job is 
  79.  * encountered in the node's spool directory. This saves
  80.  * on unneeded re-batching of uudebat and uucdebat jobs.
  81.  *
  82.  */
  83.  
  84. # include <stdio.h>
  85. #ifdef BSD
  86. #include <strings.h>
  87. #else
  88. #define index(_a,_b) strchr(_a,_b)
  89. #define rindex(_a,_b) strrchr(_a,_b)
  90. #endif
  91.  
  92. #include <string.h>
  93. #include <sys/types.h>
  94. #include <sys/stat.h>
  95. #include <sys/time.h>
  96. #include <dirent.h>
  97.  
  98. #include "patchlevel.h"
  99.  
  100. /* see main()'s flags description below for explanation of the meaning     */
  101. /* of the following three default values :                */
  102.  
  103. #define DF_MAX_BYTES         60000
  104. #define DF_MAX_CBYTES         35000
  105. #define DF_CRITICAL_SIZE    100000
  106.  
  107.  
  108. #ifndef MIN_TIME_BEFORE_BATCH
  109. #define MIN_TIME_BEFORE_BATCH    40
  110. #endif
  111.  
  112. #ifndef LINE_LENGTH
  113. #define LINE_LENGTH    512
  114. #endif
  115.  
  116. #ifndef MAX_DF2_LINES
  117. #define MAX_DF2_LINES        80
  118. #endif
  119.  
  120. #ifndef MAX_JOBS_PER_BATCH
  121. #define MAX_JOBS_PER_BATCH    100
  122. #endif
  123.  
  124. #ifndef MAX_REGISTERED_BATCHES
  125. #define MAX_REGISTERED_BATCHES    20
  126. #endif
  127.  
  128. #ifndef MAX_FILE_NAME
  129. #define MAX_FILE_NAME        ((size_t)40)
  130. #endif
  131.  
  132. char     jobnames[MAX_REGISTERED_BATCHES][MAX_FILE_NAME+1];
  133. char     df1_names[MAX_REGISTERED_BATCHES][MAX_FILE_NAME+1];
  134. char    df2_names[MAX_REGISTERED_BATCHES][MAX_FILE_NAME+1];
  135. int    jobs_uucdebat[MAX_REGISTERED_BATCHES];
  136. int    registered_batches;
  137.     
  138. char    del_jobnames[MAX_JOBS_PER_BATCH][MAX_FILE_NAME+1];
  139. char    del_df1_names[MAX_JOBS_PER_BATCH][MAX_FILE_NAME+1];
  140. char    del_df2_names[MAX_JOBS_PER_BATCH][MAX_FILE_NAME+1];
  141. int    jobs_this_batch;
  142.  
  143. char df2lines[MAX_DF2_LINES][LINE_LENGTH+1];
  144. int  df2nlines;
  145.  
  146. typedef struct _node_rec { struct _node_rec *next; char *name; } node_rec;
  147. node_rec *first_node=NULL,*curr_node=NULL,*last_node=NULL;
  148.  
  149. char batchname[L_tmpnam];
  150. char uubatch_bin_path[256];
  151.  
  152. FILE *bfile = (FILE *) NULL;
  153. FILE *cf = (FILE *) NULL;
  154. FILE *df1 = (FILE *) NULL;
  155. FILE *df2 = (FILE *) NULL;
  156.  
  157. int total_rmail_jobs;
  158.  
  159. long max_bytes, max_cbytes, critical_size, accumulated_size;
  160. int min_time_before_batch = MIN_TIME_BEFORE_BATCH;
  161.  
  162. /* General exit and diagnostics routines : */
  163.  
  164. my_exit(n)
  165. {
  166.     if (bfile) fclose(bfile);
  167.     unlink(batchname);
  168.     exit(n);
  169. }
  170.  
  171. pe_exit(s,n)
  172. char *s;
  173. int n;
  174. {
  175.     perror(s);
  176.     my_exit(n);
  177. }
  178.  
  179. #define pe_ret(_st,_nn)             \
  180.         {                 \
  181.         perror(_st);             \
  182.         if (cf) fclose(df1);        \
  183.         if (df1) fclose(df1);        \
  184.         if (df2) fclose(df2);        \
  185.         return(_nn);             \
  186.         }
  187.  
  188. pdn_exit(s,n,ne)
  189. char *s;
  190. int n,ne;
  191. {
  192.     char str[120];
  193.     sprintf(str,s,n);
  194.     fprintf(stderr,"uubatch : Diagnostic %03d: %s\n",ne,str);
  195.     my_exit(ne);
  196. }
  197.  
  198. #define pdn_ret(_st,_nn,_ne)                \
  199.         {                         \
  200.         char str[120];                 \
  201.         sprintf(str,_st,_nn);                \
  202.         fprintf(stderr,"uubatch : Diagnostic %03d: %s\n",_ne,str); \
  203.         if (cf) fclose(df1);        \
  204.         if (df1) fclose(df1);        \
  205.         if (df2) fclose(df2);        \
  206.         return (_ne);             \
  207.     }
  208.  
  209. pd(s,n)
  210. char *s;
  211. int n;
  212. {
  213.     fprintf(stderr,"uubatch : Diagnostic %03d: %s\n",n,s);
  214. }
  215.  
  216. pds(s,s1,n)
  217. char *s;
  218. int n;
  219. {
  220.     fprintf(stderr,"uubatch : Diagnostic %03d: %s %s\n",n,s,s1);
  221. }
  222.  
  223. #define pd_ret(_st,_nn)                 \
  224.     {                    \
  225.         fprintf(stderr,"uubatch : Diagnostic %03d: %s\n",_nn,_st); \
  226.         if (cf) fclose(df1);        \
  227.         if (df1) fclose(df1);        \
  228.         if (df2) fclose(df2);        \
  229.         return _nn;            \
  230.     }
  231.  
  232. #define pds_ret(_st,_nn)                 \
  233.     {                    \
  234.         fprintf(stderr,"uubatch : Diagnostic %03d: %s\n",_nn,_st); \
  235.         return _nn;            \
  236.     }
  237.  
  238. p_exit(s,n)
  239. char *s;
  240. int n;
  241. {
  242.     fprintf(stderr,"uubatch : Error %03d: %s\n",n,s);
  243.     my_exit(n);
  244. }
  245.  
  246. #define p_ret(_st,_n)                 \
  247.     {                    \
  248.         fprintf(stderr,"uubatch : Error %03d: %s\n",_n,_st); \
  249.         if (cf) fclose(df1);        \
  250.         if (df1) fclose(df1);        \
  251.         if (df2) fclose(df2);        \
  252.         return(_n);             \
  253.     }
  254.  
  255. /* Now for real code... */
  256.  
  257. #define str_eq(_s1,_s2) (!strncmp(_s1,_s2,strlen(_s2)))
  258.  
  259. /* add_node() : add a node name to a [global] linked list of node names : */
  260.  
  261. static int
  262. add_nodename(name)
  263. char *name;
  264. {
  265.     if (last_node)
  266.     {
  267.     last_node->next = (node_rec *) malloc(sizeof(node_rec));
  268.     last_node = last_node->next;
  269.     }
  270.     else
  271.     first_node = last_node = (node_rec *) malloc(sizeof(node_rec));
  272.  
  273.     last_node->next = NULL;
  274.     last_node->name = (char *) malloc(strlen(name)+1);
  275.     strcpy(last_node->name,name);
  276. }
  277.  
  278. /* lock_node() : lock a uucp node : */
  279.  
  280. static int
  281. lock_node(name)
  282. char *name;
  283. {
  284.     char command[256];
  285.     if (*uubatch_bin_path!='\0')
  286.         sprintf(command, "%s/uucplock lock %s 2> /dev/null",
  287.             uubatch_bin_path, name);
  288.     else
  289.         sprintf(command, "uucplock lock %s 2> /dev/null",
  290.             name);
  291.  
  292.     return (system(command));
  293. }
  294.  
  295. /* unlock_node() : unlock a uucp node : */
  296.  
  297. static int
  298. unlock_node(name)
  299. char *name;
  300. {
  301.     char command[256];
  302.     if (*uubatch_bin_path!='\0')
  303.         sprintf(command, "%s/uucplock unlock %s 2> /dev/null",
  304.             uubatch_bin_path, name);
  305.     else
  306.         sprintf(command, "uucplock unlock %s 2> /dev/null",
  307.             name);
  308.  
  309.     return (system(command));
  310. }
  311.  
  312.  
  313. /* get_names() :                            */
  314. /* extracts a file's local and remote names from a C.<> file line     */
  315. /* it checks the remote name for a proper prefix (passed to us).    */
  316.  
  317. get_names(line,pname,premote_name,rname_prefix)
  318. char *line,**pname,**premote_name,*rname_prefix;
  319. {
  320.     char *end_of_names;
  321.     *pname = line+2;
  322.  
  323.     if (!(*premote_name = index(*pname,' ')))
  324.     pds_ret("Oops, no space after first file name in command file.",112);
  325.  
  326.     if (!str_eq((*premote_name)+1,rname_prefix))
  327.     pds_ret("Oops, wrong remote name prefix in command file.",113);
  328.  
  329.     (*premote_name)[0] = '\0';
  330.     (*premote_name)++;
  331.  
  332.     if (!(end_of_names = index(*premote_name,' ')))
  333.     pds_ret("Oops, remote file name with no end in command file...",114);
  334.  
  335.     end_of_names[0] = '\0';
  336.  
  337.     return 0;
  338. }
  339.  
  340.  
  341.  
  342. /* batch_job() :                            */
  343. /* This routine does most of the real work. It accepts a job name    */
  344. /* and figures out if it needs to be batched. If it is an rmail job    */
  345. /* it will batch it. If the job is a uudebat or uucdebat, it will    */
  346. /* register for later re-batching, IF at least one rmail job is        */
  347. /* found in the queue.                            */
  348.  
  349. batch_job(jobname)
  350. char *jobname;
  351. {
  352.     int i;
  353.     char *df1_name, *df1_remote_name;
  354.     char *df2_name, *df2_remote_name;
  355.     char line1[LINE_LENGTH],line2[LINE_LENGTH],line[LINE_LENGTH];
  356.     struct stat df1_status;
  357.     int is_rmail,is_uudebat,is_uucdebat,line_state;
  358.  
  359.     cf = (FILE *) NULL;
  360.     df1 = (FILE *) NULL;
  361.     df2 = (FILE *) NULL;
  362.  
  363.     /* Do some nit-picking checks : */
  364.  
  365.     if (strlen(jobname) > MAX_FILE_NAME)
  366.     p_ret("jobname too long.",5);
  367.  
  368.     if (!str_eq(jobname,"C."))
  369.     p_ret("jobname must be C.<jobid>",10);
  370.  
  371.     if (!(cf = fopen(jobname,"r")))
  372.     pe_ret("Can't open specified job file",15);
  373.  
  374.     if (fgets(line1,LINE_LENGTH,cf))
  375.     {
  376.     if (!fgets(line2,LINE_LENGTH,cf))
  377.         pd_ret("Only one line in command file",20);
  378.     }
  379.     else    
  380.     p_ret("No lines in command file",25);
  381.  
  382.     if (line1[0]!='S')
  383.     pd_ret("Oops, no S in line 1 of command file",30);
  384.  
  385.     if (line2[0]!='S')
  386.     pd_ret("Oops, no S in line 2 of command file.",35);
  387.  
  388.     /* Now figure out the command file's names (local and remote), and    */
  389.     /* open it up :                            */
  390.  
  391.     if (get_names(line2,&df2_name,&df2_remote_name,"X."))
  392.     return;
  393.  
  394.     if (strlen(df2_name)>MAX_FILE_NAME)
  395.     p_ret("X. data file name too long.",40);
  396.  
  397.     if (!(df2=fopen(df2_name,"r")))
  398.     pe_ret("Can't open X. data file.",45);
  399.  
  400.  
  401.     /* scan the command file for batchable, command, while queuing the */
  402.     /* lines for later use if it turns out we want to batch this file: */
  403.  
  404.     for ( df2nlines=is_rmail=is_uudebat=is_uucdebat=0;
  405.       (fgets(line,LINE_LENGTH,df2)) && df2nlines<MAX_DF2_LINES;)
  406.     {
  407.     strcpy(df2lines[df2nlines++],line);
  408.  
  409.     /* only do checks on the beginning of file lines : */
  410.     if (line[strlen(line)-1] == '\n')
  411.     {
  412.         if (str_eq(line,"C rmail"))        is_rmail++;
  413.         if (str_eq(line,"C uudebat"))    is_uudebat++;
  414.         if (str_eq(line,"C uucdebat"))    is_uucdebat++;
  415.     }
  416.     }
  417.  
  418.     /* Figure out if all is kosher, and if there is a batchable command: */
  419.  
  420.     if (df2nlines==MAX_DF2_LINES)
  421.     pd_ret("Too many lines in X. data file.",50);
  422.  
  423.     if (!(is_rmail+is_uudebat+is_uucdebat))
  424.     pd_ret("No rmail, uudebat or uucdebat in X. data file...",55); 
  425.  
  426.     if (is_rmail+is_uudebat+is_uucdebat>1)
  427.     pd_ret("More than one rmail or uudebat, I won't do it!",60); 
  428.  
  429.     /* So it's a batchable command, let's figure out stuff about it's    */
  430.     /* data file (name, remote name, age, size) :            */
  431.  
  432.     if (get_names(line1,&df1_name,&df1_remote_name,"D."))
  433.     return;
  434.  
  435.     if (strlen(df1_name)>MAX_FILE_NAME)
  436.     p_ret("D. data fiel name too long.",65);
  437.  
  438.     if (!(df1=fopen(df1_name,"r")))
  439.     pe_ret("Can't open D. data file.",70);
  440.  
  441.     /* get status of df1 : */
  442.     fstat(fileno(df1), &df1_status);
  443.  
  444.     /* Check if it is not too old :                    */
  445.  
  446.     if (time(NULL) - df1_status.st_mtime < min_time_before_batch)
  447.     pdn_ret("Data file is too new (%d sec), next time maybe.", 
  448.         time(NULL) - df1_status.st_mtime,75);
  449.  
  450.     /* Check if it is not too big :                    */
  451.  
  452.     if ( ( (is_rmail || is_uudebat) && 
  453.            (df1_status.st_size>max_bytes)
  454.          ) ||
  455.          ( is_uucdebat && (df1_status.st_size>max_cbytes) 
  456.          )
  457.        )
  458.     pd_ret("file to big, not put in batch.",80);
  459.  
  460.  
  461.     /* If we get to this point, we know we want to batch the job.     */
  462.  
  463.  
  464.     /* BATCH it : */
  465.  
  466.     /* rmail jobs need to have their name printed on top, and         */
  467.     /* their data file contents included with # preceding each        */
  468.     /* line. For uudebat and uucdebat the we will register the        */
  469.     /* batch for later rebatching, only done if at least one rmail    */
  470.     /* job is found in the queue.                    */
  471.  
  472.     if (is_rmail)     /* for rmail jobs, batch the X. file */
  473.     {
  474.  
  475.     /* write df2's name and contents in : */
  476.     fprintf(bfile,"%s\n",df2_remote_name);
  477.     accumulated_size += strlen(df2_remote_name) + 1;
  478.  
  479.     line_state = 0;
  480.     for (i=0;i<df2nlines;i++)
  481.     {
  482.         /* if we are at the BEGINNING of a line, add a '#' to it : */
  483.  
  484.         if (!line_state)
  485.         putc('#',bfile);
  486.  
  487.         fputs(df2lines[i],bfile);
  488.  
  489.         accumulated_size += strlen(df2lines[i]) + 1;
  490.  
  491.         /* set line state for next line: if this line was \n     */
  492.         /* terminated, then the we are at the beginning of a line,  */
  493.         /* otherwise we are in the middle of one :            */
  494.  
  495.         line_state = index(df2lines[i],'\n') ? 0 : 1;
  496.     }
  497.  
  498.     /* now write df1's name and contents in : */
  499.  
  500.     fprintf(bfile,"%s\n",df1_remote_name);
  501.     accumulated_size += strlen(df1_remote_name) + 1;
  502.  
  503.     line_state = 0;
  504.     while (fgets(line,LINE_LENGTH,df1))
  505.     {
  506.         /* if we are at the BEGINNING of a line, add a '#' to it : */
  507.  
  508.         if (!line_state)
  509.         putc('#',bfile);
  510.  
  511.         fputs(line,bfile);
  512.  
  513.         accumulated_size += strlen(line) + 1;
  514.  
  515.         /* set line state for next line: if this line was \n     */
  516.         /* terminated, then the we are at the beginning of a line,  */
  517.         /* otherwise we are in the middle of one :            */
  518.  
  519.         line_state = index(line,'\n') ? 0 : 1;        
  520.     }
  521.  
  522.     /* mark job's files for deletion : */
  523.     strcpy(del_jobnames[jobs_this_batch],jobname);
  524.     strcpy(del_df1_names[jobs_this_batch],df1_name);
  525.     strcpy(del_df2_names[jobs_this_batch++],df2_name);
  526.  
  527.     total_rmail_jobs++;
  528.     }
  529.     else 
  530.     if (registered_batches<MAX_REGISTERED_BATCHES)
  531.     {
  532.         /* Register the batch for later re-batching if usefull.    */
  533.         /* Note that the are not likely to be many batch jobs    */
  534.         /* eligable for rebatching (in terms of size and age)    */
  535.         /* and that there is no harm in not registering some    */
  536.         /* of them. They will be picked up by later runs    */
  537.         /* if too many exist now...                */
  538.  
  539.         strcpy(jobnames[registered_batches],jobname);
  540.         strcpy(df1_names[registered_batches],df1_name);
  541.         strcpy(df2_names[registered_batches],df2_name);
  542.         jobs_uucdebat[registered_batches++] = is_uucdebat;
  543.     }
  544.  
  545.     /* close cf,df1,df2 we don't them it any more : */
  546.  
  547.     if (cf) fclose(cf);
  548.     if (df1) fclose(df1);
  549.     if (df2) fclose(df2);
  550.  
  551.     return 0;
  552. }
  553.  
  554. /* append_batch() :                            */
  555. /* This function will re-batch a uudebat or uucdebat into the given    */
  556. /* batch (batchname). uucdebat jobs are uncompressed before they are    */
  557. /* appended to the batch.                        */
  558.  
  559. append_batch(jobname,df1_name,df2_name,is_uucdebat,batchname)
  560. char    *df1_name,
  561.     *df2_name,
  562.     *batchname,
  563.     *jobname;
  564.  
  565. int    is_uucdebat;
  566. {
  567.     char cmdstr[256];
  568.  
  569.     /* Prepare the command to append batch contents to batch file :  */
  570.  
  571.     if (is_uucdebat)
  572.     sprintf(cmdstr, "uncompress -c < %s >> %s", df1_name, batchname);
  573.     else
  574.     sprintf(cmdstr, "cat %s >> %s", df1_name, batchname);
  575.  
  576.     /* execute the command, exit if non-zero exit status : */
  577.  
  578.     if (system(cmdstr))
  579.     p_exit("error while uncompressing/appending batch",93);
  580.  
  581.     /* mark job's files for deletion : */
  582.  
  583.     strcpy(del_jobnames[jobs_this_batch],jobname);
  584.     strcpy(del_df1_names[jobs_this_batch],df1_name);
  585.     strcpy(del_df2_names[jobs_this_batch++],df2_name);
  586. }
  587.  
  588. /* send_batch() :                            */
  589. /* This function will send off a batch. It will handle both compressed    */
  590. /* and uncompressed batching, choosing between uudebat and uucdebat     */
  591. /* as the uux'ed commands. Once successfully uux'ed, the batch's data    */
  592. /* file and the data files of all jobs batched in it are deleted.    */
  593.  
  594. send_batch(batchname,nodename,compression_mode)
  595. char *batchname,*nodename;
  596. int compression_mode;
  597. {
  598.     char cmdstr[256];
  599.     int i;
  600.  
  601.     fprintf(stderr,"uubatch : sending batch...");
  602.  
  603.     /* Prepare the uux command : */
  604.  
  605.     if (compression_mode)
  606.     sprintf(cmdstr, "compress -c %s | uux - -r %s!uucdebat",
  607.             batchname, nodename);
  608.     else
  609.     sprintf(cmdstr, "uux - -r %s!uudebat < %s",
  610.             nodename, batchname);
  611.     
  612.     /* Execute it : */
  613.  
  614.     if (system(cmdstr))
  615.     p_exit("error while uux'ing batch",115);
  616.  
  617.     /* Remove batch temporary data file : */
  618.  
  619.     if (unlink(batchname))
  620.     p_exit("error while removing batch file",120);
  621.  
  622.     /* Remove all data files of jobs batched in this batch : */
  623.  
  624.     for (i=0;i<jobs_this_batch;i++)
  625.     {
  626.     if (unlink(del_jobnames[i]) || unlink(del_df1_names[i]) ||
  627.         unlink(del_df2_names[i]) )
  628.         p_exit("error while deleting batched files",125);
  629.  
  630.     }
  631.  
  632.     /* restart job count : */
  633.  
  634.     jobs_this_batch = 0;
  635.  
  636.     fprintf(stderr,"batch sent.\n");
  637. }
  638.  
  639. static int
  640. digest_args(argc,argv,compression_mode)
  641. int argc;
  642. char **argv;
  643. int *compression_mode;
  644. {
  645.     int node_spec=0;
  646.     char c,*p;
  647.  
  648.     /* the following externs are defined by getopt(3), look them up : */
  649.  
  650.     extern char *optarg;
  651.     extern int optind;
  652.  
  653.     *compression_mode = 0;
  654.     max_bytes = DF_MAX_BYTES;
  655.     max_cbytes = DF_MAX_CBYTES;
  656.     critical_size = DF_CRITICAL_SIZE;
  657.  
  658.     /* extract the path to uubatch's binary directory. uucplock is     */
  659.     /* assumed to be there :                         */
  660.  
  661.     strcpy(uubatch_bin_path,argv[0]);
  662.     if (p = rindex(uubatch_bin_path,'/'))
  663.     *p='\0';
  664.  
  665.     /* parse options : */
  666.  
  667.     while((c = getopt(argc, argv, "cB:C:S:T:")) != -1)
  668.     switch(c) 
  669.     {
  670.     case 'c' : *compression_mode = 1;             break;
  671.     case 'B' : max_bytes = atoi(optarg);             break;
  672.     case 'C' : max_cbytes = atoi(optarg);             break;
  673.     case 'S' : critical_size = atoi(optarg);         break;
  674.     case 'T' : min_time_before_batch = atoi(optarg);     break;
  675.     case '?' : return 1;
  676.     }
  677.  
  678.     for (; optind < argc; optind++)
  679.     {
  680.     add_nodename(argv[optind]);
  681.     node_spec = 1;
  682.     }
  683.  
  684.    /* return error (true value) if no node or no bin dir is specified : */
  685.  
  686.    return (!node_spec); 
  687. }
  688.  
  689.  
  690. /* main() :                                */
  691. /*                                    */
  692. /* This is the main program. See top of file to find out what it does    */
  693. /*                                    */
  694. /* Usage :                                */
  695. /*    uubatch [-c] [-B n] [-C n] [-S n] [-T n] node [node2...]    */
  696. /*                                    */
  697.  
  698. main(argc,argv)
  699. int argc;
  700. char **argv;
  701. {
  702.     int i,compression_mode;
  703.     char *jobname,*nodename,pathname[256];
  704.     struct stat bfile_status;
  705.     DIR *dirp;
  706.     struct dirent *dp;
  707.  
  708.     /* do nit-picking checks and argument extraction : */
  709.  
  710.     if (digest_args(argc,argv,&compression_mode))
  711.     p_exit(
  712.     "Usage:\nuubatch [-c] [-B n] [-C n] [-S n] [-T n] node [node2...]"
  713.     ,5);
  714.  
  715.  
  716.     /* loop on all node names specified in args, and do rmail batching on */
  717.     /* each one :                               */
  718.  
  719.     for(curr_node=first_node;curr_node;curr_node=curr_node->next)
  720.     {
  721.     nodename = curr_node->name;
  722.  
  723.     fprintf(stderr,"uubatch : Doing node %s\n",nodename);
  724.  
  725.     /* construct node's spool directory path :             */
  726.  
  727.     strcpy(pathname,UUCP_SPOOL_DIR);
  728.     strcat(pathname,"/");
  729.     strcat(pathname,nodename);
  730.         
  731.     /* cd to node's spool directory :                 */
  732.  
  733.     if (chdir(pathname))
  734.         pe_exit("Cannot cd to node's spool directory",78);
  735.  
  736.     /* put a uucp lock on the node, continue only if lock succeeds : */
  737.  
  738.     if (!lock_node(nodename))
  739.     {
  740.         
  741.         tmpnam(batchname);    /* Get a name for the batch file.    */
  742.         
  743.         accumulated_size = total_rmail_jobs = 
  744.         jobs_this_batch = registered_batches = 0;
  745.         
  746.         /* open the batch file :                     */
  747.  
  748.         if (!(bfile=fopen(batchname,"w")))
  749.         pe_exit("Can't temporary batch file",82);
  750.         
  751.         /* open the spool directory in order to search it :     */
  752.  
  753.         dirp = opendir(pathname);
  754.         
  755.         /* loop on all jobs (searching the for C.* files):         */
  756.  
  757.         while ( dp = readdir(dirp) )
  758.         if (str_eq(jobname=dp->d_name,"C."))
  759.         {
  760.             fprintf(stderr,"uubatch : Doing file %s\n",jobname);
  761.         
  762.             /* If batch file if "full", send it and reset it :    */
  763.  
  764.             if ( (accumulated_size>critical_size) ||
  765.                  (jobs_this_batch>MAX_JOBS_PER_BATCH) )
  766.             {
  767.             /* close the batch file, send it off, and open     */
  768.             /* a new one :                     */
  769.  
  770.             if (fclose(bfile))
  771.                 pe_exit("error closing batch file",85);
  772.             send_batch(batchname,nodename,compression_mode);
  773.             if (!(bfile=fopen(batchname,"w")))
  774.                 pe_exit("Can't temporary batch file",90);
  775.             accumulated_size = 0;
  776.             }
  777.         
  778.             /* Attempt to batch the current job in the loop :    */
  779.  
  780.             batch_job(jobname);    
  781.         }
  782.         
  783.         /* now close the batch file : */
  784.  
  785.         if (fclose(bfile))
  786.         pe_exit("error closing batch file",95); 
  787.         
  788.         if (total_rmail_jobs) /* only re-batch registered jobs if needed.*/
  789.         {
  790.         /* loop on all registered jobs (eligable for re-batching in  */
  791.         /* terms of size and age) and re-batch them :             */
  792.  
  793.         for (i=0;i<registered_batches;i++)
  794.         {
  795.             stat(batchname,&bfile_status); /* get batch file stats. */
  796.         
  797.             /* If batch file if "full",send it off and reset it :   */
  798.  
  799.             if ( (bfile_status.st_size>critical_size) ||
  800.                  (jobs_this_batch>MAX_JOBS_PER_BATCH) )
  801.             send_batch(batchname,nodename,compression_mode);
  802.         
  803.             /* re-batch the current registered batch in the loop : */
  804.  
  805.                 append_batch(jobnames[i],df1_names[i],df2_names[i],
  806.                     jobs_uucdebat[i],batchname);
  807.         }
  808.  
  809.         stat(batchname,&bfile_status);    /* get batch file stats. */
  810.         
  811.         /* send batch if there is anything left (usually there is) :*/
  812.  
  813.         if (bfile_status.st_size>0)
  814.             send_batch(batchname,nodename,compression_mode);
  815.         }
  816.         else      /* if no rmail jobs, skipped batching : */
  817.         pd("No rmail jobs, skip batching.",100);
  818.         
  819.         if (unlock_node(nodename))
  820.         pds("unlock failed on node",nodename,110);
  821.  
  822.     } /* of if on successfull lock .*/
  823.     else
  824.         pds("lock failed on node",nodename,122);
  825.  
  826.     unlink(batchname);
  827.  
  828.     } /* of node loop. */
  829.  
  830.     /* bye bye : */
  831.  
  832.     my_exit(0);
  833. }
  834.